package org.example.storage;

import org.example.model.MonitoringMetrics;
import org.example.model.Session;
import org.example.monitor.HealthStatus;
import org.example.monitor.Monitorable;

import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.atomic.AtomicLong;

public class SessionStorage implements Monitorable {
    // 聚簇索引: sessionKey 作为主键，存储完整的 Session 对象
    private final Map<String, Session> clusteredIndex = new ConcurrentHashMap<>();

    // 非聚簇索引: customerId 作为键，只存储 sessionKey 引用
    private final Map<Integer, String> nonClusteredIndex = new ConcurrentHashMap<>();

    // LRU 链表：使用 ConcurrentLinkedDeque 替代 LinkedHashSet + 锁
    private final ConcurrentLinkedDeque<String> lruAccessOrder = new ConcurrentLinkedDeque<>();

    // 按过期时间排序的会话（用于快速清理）
    private final ConcurrentSkipListMap<Instant, Set<String>> sessionsByExpiry = new ConcurrentSkipListMap<>();

    // 配置
    private final int maxCapacity;
    private final float evictionThreshold;

    // 统计信息 - 使用 AtomicLong
    private final AtomicLong totalSessionsCreated = new AtomicLong(0);
    private final AtomicLong totalSessionsExpired = new AtomicLong(0);
    private final AtomicLong totalSessionsEvicted = new AtomicLong(0);

    //    private long cacheHits = 0;
//    private long cacheMisses = 0;
    public SessionStorage() {
        this(10, 0.9f); // 默认最大容量10，阈值90%
    }


    public SessionStorage(int maxCapacity, float evictionThreshold) {
        this.maxCapacity = maxCapacity;
        // 防御编程  确保系统稳定
        this.evictionThreshold = Math.min(evictionThreshold, 1.0f);
        System.out.println("SessionStorage initialized with maxCapacity: " + maxCapacity +
                ", evictionThreshold: " + (evictionThreshold * 100) + "%");
    }

    /**
     * 保存会话 并更新缓存 与索引 更新过期时间
     */
    public void saveSession(Session session) {
        checkAndEvictIfNeeded();

        String sessionKey = session.getSessionKey();
        Integer customerId = session.getCustomerId();

        // 如果客户已有会话，先移除旧的
        String existingSessionKey = nonClusteredIndex.get(customerId);
        if (existingSessionKey != null) {
            removeSessionByKey(existingSessionKey);
        }

        // 1. 保存到聚簇索引（完整对象）
        clusteredIndex.put(sessionKey, session);

        // 2. 保存到非聚簇索引（仅引用）
        nonClusteredIndex.put(customerId, sessionKey);

        // 更新LRU访问顺序
        updateAccessOrder(sessionKey);

        // 添加到过期时间索引
        sessionsByExpiry
                .computeIfAbsent(session.getExpiryTime(), k -> ConcurrentHashMap.newKeySet())
                .add(sessionKey);

        totalSessionsCreated.incrementAndGet();
    }

    /**
     * 根据客户ID查找会话 - 通过非聚簇索引找到引用，再到聚簇索引获取完整对象
     */
    public Optional<Session> findSessionByCustomerId(int customerId) {
        // 1. 查询非聚簇索引获取引用
        String sessionKey = nonClusteredIndex.get(customerId);
        if (sessionKey == null) {
            return Optional.empty();
        }

        // 2. 通过聚簇索引获取完整对象
        return findSessionByKey(sessionKey);
    }

    /**
     * 根据会话密钥查找会话 - 直接访问聚簇索引
     */
    public Optional<Session> findSessionByKey(String sessionKey) {
        Session session = clusteredIndex.get(sessionKey);
        if (session != null && session.isValid()) {
            // 更新最后访问时间
            session.updateLastAccessed();
            // 更新LRU顺序
            updateAccessOrder(sessionKey);
            return Optional.of(session);
        } else if (session != null && !session.isValid()) {
            // 自动清理过期会话
            removeSessionByKey(sessionKey);
        }
        return Optional.empty();
    }

    /**
     * 移除会话
     */
    public boolean removeSession(int customerId, String sessionKey) {
        // 验证会话存在且匹配
        String expectedSessionKey = nonClusteredIndex.get(customerId);
        if (expectedSessionKey != null && expectedSessionKey.equals(sessionKey)) {
            removeSessionByKey(sessionKey);
            return true;
        }
        return false;
    }

    /**
     * 获取活跃会话数量
     */
    public int getActiveSessionCount() {
        return (int) clusteredIndex.values().stream()
                .filter(Session::isValid)
                .count();
    }

    /**
     * 清理过期会话
     */
    public void cleanupExpiredSessions() {
        Instant now = Instant.now();
        int cleanedCount = 0;

        // 获取所有已过期的会话
        var expiredSessions = sessionsByExpiry.headMap(now);

        for (Set<String> sessionKeys : expiredSessions.values()) {
            for (String sessionKey : sessionKeys) {
                Session session = clusteredIndex.get(sessionKey);
                if (session != null && !session.isValid()) {
                    removeSessionByKey(sessionKey);
                    cleanedCount++;
                    totalSessionsExpired.incrementAndGet();
                }
            }
        }

        // 清空已过期的部分
        expiredSessions.clear();

        if (cleanedCount > 0) {
            System.out.println("Cleaned " + cleanedCount + " expired sessions");
        }
    }

    /**
     * 强制清理所有会话（用于测试）
     */
    public void clear() {
        clusteredIndex.clear();
        nonClusteredIndex.clear();
        sessionsByExpiry.clear();
        lruAccessOrder.clear();
        totalSessionsCreated.set(0);
        totalSessionsExpired.set(0);
        totalSessionsEvicted.set(0);
    }

    /**
     * 获取存储统计信息
     */
    @Override
    public void executeMonitorCycle() {
        cleanupExpiredSessions();
        Map<String, Object> stats = new HashMap<>();
        stats.put("activeSessions", getActiveSessionCount());
        stats.put("totalSessionsCreated", totalSessionsCreated.get());
        stats.put("totalSessionsExpired", totalSessionsExpired.get());
        stats.put("totalSessionsEvicted", totalSessionsEvicted.get());
        stats.put("clusteredIndexSize", clusteredIndex.size());
        stats.put("nonClusteredIndexSize", nonClusteredIndex.size());

        System.out.println("SessionStorage statistics: " + stats);
    }


    @Override
    public String getComponentName() {
        return "SessionStorage";
    }

    @Override
    public MonitoringMetrics getMetrics() {
        return null;
    }

    @Override
    public HealthStatus getHealthStatus() {
        return HealthStatus.HEALTHY;
    }

    /**
     * 更新LRU访问顺序 - 无锁操作
     */
    private void updateAccessOrder(String sessionKey) {
        // 简单的LRU更新：先移除再添加到头部
        lruAccessOrder.remove(sessionKey);
        lruAccessOrder.addFirst(sessionKey);
    }

    /**
     * 通过sessionKey移除会话（内部方法）
     */
    private void removeSessionByKey(String sessionKey) {
        Session session = clusteredIndex.get(sessionKey);
        if (session == null) {
            return;
        }

        // 从聚簇索引移除
        clusteredIndex.remove(sessionKey);

        // 从非聚簇索引移除
        nonClusteredIndex.remove(session.getCustomerId());

        // 从LRU链表中移除
        lruAccessOrder.remove(sessionKey);

        // 从过期时间索引中移除
        Set<String> sessionKeys = sessionsByExpiry.get(session.getExpiryTime());
        if (sessionKeys != null) {
            sessionKeys.remove(sessionKey);
            if (sessionKeys.isEmpty()) {
                sessionsByExpiry.remove(session.getExpiryTime());
            }
        }
    }

    /**
     * 淘汰最近最少使用的会话
     */
    private void evictLRUSessions(int count) {
        int evicted = 0;

        while (evicted < count && !lruAccessOrder.isEmpty()) {
            String sessionKey = lruAccessOrder.pollLast();
            Session session = clusteredIndex.get(sessionKey);

            if (session != null) {
                removeSessionByKey(sessionKey);
                evicted++;
                totalSessionsEvicted.incrementAndGet();

                System.out.println("Evicted session: " + sessionKey +
                        " (customer: " + session.getCustomerId() + ")");
            }
        }

        System.out.println("SessionStorage: Evicted " + evicted + " LRU sessions");
    }

    /**
     * 检查容量并执行淘汰
     */
    private void checkAndEvictIfNeeded() {
        int currentSize = clusteredIndex.size();
        if (currentSize >= maxCapacity * evictionThreshold) {
            int targetSize = (int) (maxCapacity * 0.8);
            int toEvict = currentSize - targetSize;

            if (toEvict > 0) {
                System.out.println("SessionStorage: Capacity threshold reached (" + currentSize +
                        "/" + maxCapacity + "), evicting " + toEvict + " sessions");
                evictLRUSessions(toEvict);
            }
        }
    }
}